﻿// 3D方框透视示例源码
// Author: 阿茵Ayin
/*
	免责声明：
	仅供学习交流，如果用来做违f的事情和作者本人无关

	注：（必看）

	WorldToScreen算法用的是列矩，如果你找到的是横距请自行写一个矩阵转置函数

	示例游戏是迷你World，高度轴为Y轴，其他例如cs_go这类Z轴是高度轴的这个算法需要改动

*/

#include "BoxESP.h"

// 求算方框点在偏航角变了之后新的点的位置
bool RotateTransform(float** pvm, Vector3F opc, float angle, int radius, float viewWidth, float viewHeight, Vector2F& transformed) {
	
	// 弧度转角度，因为api问题不能直接用角度
	float radian = PI() / 180 * angle;

	// 求出偏航角变化之后新的点的世界空间坐标
	Vector3F newPoint = { 0.0f, 0.0f, 0.0f };
	newPoint.x = opc.x + cos(radian) * radius;
	newPoint.y = opc.y + sin(radian) * radius;
	newPoint.z = opc.z;

	// WorldToScreen，把NewPoint从世界空间坐标变换到屏幕坐标
	// 再次提醒，本示例游戏的高度轴是Y轴，算法会有所变化
	float clipW = pvm[4][1] * newPoint.x + pvm[4][2] * newPoint.z + newPoint.y * pvm[4][3] + pvm[4][4];

	// 如果有任意一个点位于我们的视锥体之外，则不绘制这个Box，因为八个点少了任何一个点都会导致线条错乱
	if (clipW < 0.01f) return false;

	float scale = 1 / clipW;

	transformed.x = viewWidth + (pvm[1][1] * newPoint.x + pvm[1][2] * newPoint.z + newPoint.y * pvm[1][3] + pvm[1][4]) * scale * viewWidth;
	transformed.y = viewHeight - (pvm[2][1] * newPoint.x + pvm[2][2] * newPoint.z + newPoint.y * pvm[2][3] + pvm[2][4]) * scale * viewHeight;

	return true;
}

// 绘制3D方框
bool DrawBox(float** pvm, Vector3F opc, float height, Rotation rotation, float radius, Color4F color, int thickness, float viewWidth, float viewHeight) {

	// Botton Point 1，脚下第一个点
	Vector2F bp1 = { 0.0f, 0.0f };
	Vector2F bp2 = { 0.0f, 0.0f };
	Vector2F bp3 = { 0.0f, 0.0f };
	Vector2F bp4 = { 0.0f, 0.0f };

	// Top Origin Point，盒子顶部原点的坐标
	Vector3F top = { 0.0f, 0.0f, 0.0f };

	// Top Point 1，头上第一个点
	Vector2F tp1 = { 0.0f, 0.0f };
	Vector2F tp2 = { 0.0f, 0.0f };
	Vector2F tp3 = { 0.0f, 0.0f };
	Vector2F tp4 = { 0.0f, 0.0f };

	// 求算并画出人物脚下四个点
	if (
		RotateTransform(pvm, opc, rotation.yaw + 45, radius, viewWidth, viewHeight, bp1) &&
		RotateTransform(pvm, opc, rotation.yaw + 135, radius, viewWidth, viewHeight, bp2) && 
		RotateTransform(pvm, opc, rotation.yaw + 225, radius, viewWidth, viewHeight, bp3) &&
		RotateTransform(pvm, opc, rotation.yaw + 315, radius, viewWidth, viewHeight, bp4)
		) {
	
		// 连接脚下四个点
		DrawLine(bp1, bp2, thickness, color);
		DrawLine(bp2, bp3, thickness, color);
		DrawLine(bp3, bp4, thickness, color);
		DrawLine(bp4, bp1, thickness, color);
	
	}
	else { return false; }

	// 顶部原点坐标
	top.x = opc.x;
	top.y = opc.y;
	top.z = opc.z + height;

	// 求算并画出人物头上四个点
	if (
		RotateTransform(pvm, opc, rotation.yaw + 45, radius, viewWidth, viewHeight, tp1) &&
		RotateTransform(pvm, opc, rotation.yaw + 135, radius, viewWidth, viewHeight, tp2) &&
		RotateTransform(pvm, opc, rotation.yaw + 225, radius, viewWidth, viewHeight, tp3) &&
		RotateTransform(pvm, opc, rotation.yaw + 315, radius, viewWidth, viewHeight, tp4)
		) {

		// 连接头上四个点
		DrawLine(tp1, tp2, thickness, color);
		DrawLine(tp2, tp3, thickness, color);
		DrawLine(tp3, tp4, thickness, color);
		DrawLine(tp4, tp1, thickness, color);

	}
	else { return false; }

	// 画出剩余四条竖直的线
	DrawLine(bp1, tp1, thickness, color);
	DrawLine(bp2, tp2, thickness, color);
	DrawLine(bp3, tp3, thickness, color);
	DrawLine(bp4, tp4, thickness, color);

	return true;
}
